#include "src/net/EventLoop.h"
#include "src/net/InetAddress.h"
#include "src/net/TcpClient.h"
#include "src/net/TcpServer.h"

#include "gtest/gtest.h"
#include <future>
#include <thread>

using namespace sola;
using namespace sola::net;

TEST(IntegrationTest, EchoTest) {
    // 1. Setup server in a separate thread
    // EventLoop serverLoop; // <-- DO NOT create here
    InetAddress listenAddr(12345);

    std::promise<EventLoop*> serverLoopPromise;
    auto                     serverLoopFuture = serverLoopPromise.get_future();

    std::thread serverThread([&]() {
        EventLoop serverLoop; // <-- CREATE EventLoop inside the thread
        TcpServer server(&serverLoop, listenAddr, "EchoServer");

        // Server echoes whatever it receives
        server.setMessageCallback([](const TcpConnectionPtr& conn, Buffer* buf, Timestamp) { conn->send(buf); });

        server.start();
        serverLoopPromise.set_value(&serverLoop); // Signal that server is ready
        serverLoop.loop();
    });

    // Wait for server to be ready and get its loop pointer
    EventLoop* serverLoopPtr = serverLoopFuture.get();

    // 2. Setup client in the main thread
    EventLoop clientLoop;
    TcpClient client(&clientLoop, listenAddr, "EchoClient");

    std::string               message = "Hello, ReactorCore!";
    std::promise<std::string> messagePromise;
    auto                      messageFuture = messagePromise.get_future();
    bool                      disconnected  = false;

    client.setConnectionCallback([&](const TcpConnectionPtr& conn) {
        if (conn->connected()) {
            conn->send(message);
        } else {
            // Connection closed
            disconnected = true;
            // Quit the client loop once disconnected, to ensure the test terminates
            clientLoop.quit();
        }
    });

    client.setMessageCallback([&](const TcpConnectionPtr& conn, Buffer* buf, Timestamp) {
        std::string received = buf->retrieveAllAsString();
        messagePromise.set_value(received);
        conn->shutdown(); // Gracefully shutdown after receiving echo
    });

    client.connect();
    clientLoop.loop();

    // 3. Validate results
    auto status = messageFuture.wait_for(std::chrono::seconds(0));
    ASSERT_EQ(status, std::future_status::ready) << "Client did not receive message.";

    std::string receivedMessage = messageFuture.get();
    EXPECT_EQ(receivedMessage, message);
    EXPECT_TRUE(disconnected);

    // 4. Teardown
    serverLoopPtr->quit();
    serverThread.join();
}